<?php

namespace App\Services\Applet\User;

use App\Exceptions\ApiException;
use App\Exceptions\LoginException;
use App\Exceptions\OtherException;
use App\Exceptions\SmsException;
use App\Exceptions\SponsorException;
use App\Exceptions\UserException;
use App\Helpers\MvcHelper;
use App\Helpers\Redis\RedisHelper;
use App\Models\Applet\Sponsor\UserSlaughterRecordsModel;
use App\Models\Applet\User\UserIntegralRecordsModel;
use App\Services\Applet\Other\SmsService;
use Exception;

class UserService extends BaseUserService
{

    const WHITE_LIST_PHONE_DEFAULT_CODE = 123456;    // 白名单手机号对应的默认验证码

    // 默认头像
    const AVATAR_DEFAULT = 'https://www.baidu.com/link?url=OS-2zUgsz2uq2AjPyw01aq8j8fg15SjBLtzBpjMilm7R8zeXeLazdmaLK2uLkB53Yfio4TYfFiKFNKBEvKvqpaYM2n18gZYWEiZhLILwAFaEE3zJrzK2npiDhUmQG-VfzqSmzbXVcjQQ2iJqm3-Vx-i4OmIask07InJvc1c1mIKfolo5Kx0BPg-oLpDaQ9KXdYlnuKim48jZZhC9OB6yIzIxPhzqlgi8CxaPCh3yJLqNFGie9YUgc0_nEuED50MU-6lDQxIPKTzijV3-pKQBxcJWd8ZRXn1_A6uvp3ttcGoi5gQ2XLZj1R_QBne9_N7z4jRQEv1qq2CzcP2JECy79h2uvpkxzRCpyPyeawy8lp0zhNnvLNfI-vb6ZvwjEKtp6mgvE9yDR_3bdlTjKTotD6pblgL92PUYnbHIBQdPK1cC3iE4LbCsqRkEDU808NL4746QA_Nw52kIjHewlxI5IOL_FopJeEOhisIdRewKDz2hfWTop88bKph-q9fM0Z2Chwfq3HetV-phSCBCIFtN3ge0x75XlOI6jCny2CF_EL_v4cNMzQqf63dI0Em3EgSL7mpJ8eITivDg_CFy-eUversgadIj2o5X5c4EduufOznyYVb4hKUMAjjSigZTL3EM4g7xxPxpwTZplvXwq4YlKtfx3QWkyH-Oy_oRxlw7nAy&wd=&eqid=a2ffbe780684aa3e000000066767a77b';

    /**
     * 发送短信验证码
     * @param array $params
     * @return void
     * @throws SmsException
     * @throws ApiException
     * @throws Exception
     */
    public function sendVerifyCode(array $params)
    {
        $tel = $params['phone'];
        // 验证手机号
        $this->checkTel($tel);

        // 检测白名单
        $check = $this->checkTelWhiteList($tel);
        if ($check) {
            // 获取验证码
            $code = self::WHITE_LIST_PHONE_DEFAULT_CODE;
        } else {
            // 设置尝试次数
            $this->checkCodeTryCount($tel, function () {
                throw new SmsException('获取登录验证码频率过高，请稍后重试', SmsException::CODE_1101);
            });
            // 获取验证码
            $code = getValidCode();
        }

        // 设置验证码
        $this->setCodeCache($tel, $code);

        // 发送验证码
        !$check && $this->getSmsService()->sendCode(SmsService::CODE_TYPE_CLIENT_LOGIN, $tel, $code);
    }

    /**
     * @return void
     * @throws Exception
     */
    public function register(array $params)
    {
        // 检测验证码尝试次数
        $this->checkCodeCheckCount(SmsService::CODE_TYPE_CLIENT_LOGIN, $params['phone'], function () {
            throw new SmsException('操作频率过高，请稍后重试', SmsException::CODE_1301);
        });

        // 检测登录验证码
        $this->checkCodeCache($params['phone'], $params['verifyCode']);

        // 验证两次密码是否一致
        if ($params['password'] != $params['qrPassword']) {
            throw new UserException('密码不一致', UserException::CODE_1000);
        }

        // 判断用户是否存在
        $userData = $this->getUserModel()->getItemByPhone($params['phone']);
        if ($userData) {
            throw new UserException('用户已存在', UserException::CODE_1000);
        }

        // 查询邀请人是否存在
        if (!empty($params['inviteCode'])) {
            $inviteUser = $this->getUserModel()->getItemByInviteCode($params['inviteCode']);
            if (empty($inviteUser)) {
                throw new UserException('邀请人不存在', UserException::CODE_1002);
            }
        }

        // 获取授权token
        $code2SessionData = $this->getWechatApi()->jsCode2Session($params['code']);

        // 密码加密
        $password = encryption($params['password']);

        // 生成邀请码
        $inviteCode = createInviteCode();

        $addUserData = [
            'phone' => $params['phone'],
            'nickname' => "用户" . substr($params['phone'], 7, 4),
            'avatar' => $params['avatar'] ?? self::AVATAR_DEFAULT,
            'password' => $password,
            'invite_user_id' => $inviteUser['id'] ?? 0,
            'openid' => $code2SessionData['openid'],
            'unionid' => $code2SessionData['unionid'],
            'invite_code' => $inviteCode,
        ];

        // 添加用户
        $this->getUserModel()->addItem($addUserData);
    }

    /**
     * 登录
     * @param array $params
     * @return array
     * @throws LoginException
     * @throws Exception
     */
    public function login(array $params): array
    {
        // 查询用户信息
        $userData = $this->getUserModel()->getItemByPhone($params['phone'], [
            'id', 'nickname', 'password', 'status',
        ]);
        if (empty($userData)) {
            throw new LoginException('用户名或密码错误', LoginException::CODE_1001);
        }
        if (!encryptionCheck($params['password'], $userData['password'])) {
            throw new LoginException('用户名或密码错误', LoginException::CODE_1001);
        }
        if ($userData['status'] != 0) {
            throw new LoginException('登陆失败! 用户被禁止登录!', LoginException::CODE_1002);
        }

        $userInfo = [
            'userId' => $userData['id'],
            'nickName' => $userData['nickname'],
        ];

        // 生成登录 token       @tips 用户权限修改后需要重新登录
        $token = generateJwt($userInfo, self::USER_TYPE_APPLET);

        return [
            'user' => $userInfo,
            'token' => $token,
        ];
    }

    /**
     * 退出登录 拉黑jwt
     * @return void
     */
    public function logout()
    {
        $redisKey = RedisHelper::makeKey(RedisHelper::STRING_CLIENT_JWT_BLACK_LIST, MvcHelper::getLoginToken());
        jwtJoinBlackList($redisKey, MvcHelper::getLoginTokenExpire());
    }

    /**
     * 重置密码
     * @return void
     * @throws LoginException
     * @throws UserException
     * @throws Exception
     */
    public function resetPassword(array $params)
    {
        // 检测验证码尝试次数
        $this->checkCodeCheckCount(SmsService::CODE_TYPE_CLIENT_LOGIN, $params['phone'], function () {
            throw new SmsException('操作频率过高，请稍后重试', SmsException::CODE_1301);
        });

        // 检测登录验证码
        $this->checkCodeCache($params['phone'], $params['verifyCode']);

        // 验证两次密码是否一致
        if ($params['password'] != $params['qrPassword']) {
            throw new UserException('密码不一致', UserException::CODE_1000);
        }

        // 判断用户是否存在
        $userData = $this->getUserModel()->getItemByPhone($params['phone'], [
            'id', 'status',
        ]);
        if (empty($userData)) {
            throw new UserException('用户不存在', UserException::CODE_1003);
        }
        if ($userData['status'] != 0) {
            throw new LoginException('登陆失败! 用户被禁止登录!', LoginException::CODE_1002);
        }

        // 重新生成密码
        $password = encryption($params['password']);

        $this->getUserModel()->updateItemById($userData['id'], [
            'password' => $password,
        ]);
    }

    /**
     * 用户详情
     * @return array
     * @throws Exception
     */
    public function userInfo(): array
    {
        $userInfo = $this->getUserModel()->getItemById(MvcHelper::getLoginUserId(), [
            'id', 'nickname', 'avatar', 'integral', 'invite_code'
        ]);

        return [
            'userId' => $userInfo['id'],
            'nickname' => $userInfo['nickname'],
            'avatar' => $userInfo['avatar'],
            'integral' => $userInfo['integral'],
            'inviteCode' => $userInfo['invite_code'],
        ];
    }

    /**
     * 验证用户
     * @param int $userId
     * @return array
     * @throws Exception
     */
    public function getCheckUserInfo(int $userId): array
    {
        return $this->getUserModel()->getItemById($userId, [
            'id', 'nickname', 'phone', 'status'
        ]);
    }

    /**
     * 订单总数列表
     * @return array
     * @throws Exception
     */
    public function orderTotal(): array
    {
        // 用户认养订单数量
        $sponsorOrderTotal = $this->getSponsorMasterOrderModel()->getTotalByUserId(MvcHelper::getLoginUserId());
        // 用户商品订单数量
        $shopOrderTotal = $this->getShopMasterOrderModel()->getTotalByUserId(MvcHelper::getLoginUserId());
        return [
            'sponsorOrderTotal' => $sponsorOrderTotal,
            'shopOrderTotal' => $shopOrderTotal,
        ];
    }

    /**
     * 认养列表
     * @param array $params
     * @return array
     * @throws Exception
     */
    public function sponsorList(array $params): array
    {
        // 查询认养列表总条数
        $total = $this->getUserSponsorModel()->getTotalByUserId(MvcHelper::getLoginUserId(), $params);

        // 查询认养总数量
        $totalQuantity = $this->getUserSponsorModel()->getTotalQuantityByUserId(MvcHelper::getLoginUserId());

        // 查询认养列表
        $userSponsorList = $this->getUserSponsorModel()->getItemsByUserId(MvcHelper::getLoginUserId(), $params, [
            'id', 'ranch_id', 'order_id', 'goods_id', 'name', 'cover_img', 'province_id', 'city_id', 'region_id', 'cycle', 'start_time', 'quantity', 'status'
        ]);
        $ranchIds = array_column($userSponsorList, 'ranch_id');

        // 查询牧场
        $ranchList = $this->getStoreRanchModel()->getItemsByIds($ranchIds, ['id', 'province_id', 'city_id', 'region_id']);
        $ranchListMap = array_column($ranchList, null, 'id');

        // 查询地区
        $provinceIds = array_column($ranchList, 'province_id');
        $cityIds = array_column($ranchList, 'city_id');
        $regionIds = array_column($ranchList, 'region_id');

        $areaIds = array_merge($provinceIds, $cityIds, $regionIds);

        // 查询地区
        $areaList = $this->getAreaModel()->getAreaNameListByCodeList($areaIds);
        $areaListMap = array_column($areaList, 'name', 'code');

        $orderIds = array_column($userSponsorList, 'order_id');
        $goodsIds = array_column($userSponsorList, 'goods_id');

        // 查询订单
        $orderGoodsList = $this->getSponsorOrderGoodsModel()->getItemByOrderIdsAndGoodsIds($orderIds, $goodsIds, [
            'id', 'order_id', 'goods_id', 'price'
        ]);
        $orderGoodsListMap = [];
        foreach ($orderGoodsList as $item) {
            $key = $item['order_id'] . '-' . $item['goods_id'];
            $orderGoodsListMap[$key] = $item['price'];
        }

        $list = [];
        foreach ($userSponsorList as $item) {
            $ranch = $ranchListMap[$item['ranch_id']] ?? [];

            //地区名称
            $areaName = '';
            if ($ranch) {
                $provinceName = $areaListMap[$ranch['province_id']] ?? '';
                $cityName = $areaListMap[$ranch['city_id']] ?? '';
                $countyName = $areaListMap[$ranch['region_id']] ?? '';
                $areaName = $provinceName . '-' . $cityName . '-' . $countyName;
            }

            // 周期(周)
            $cycle = ceil($item['cycle'] / (7 * 24 * 60 * 60));
            // 已养周期
            $useCycle = floor((time() - strtotime($item['start_time'])) / (7 * 24 * 60 * 60));

            $endDate = date('Y-m-d', strtotime($item['start_time']) + $item['cycle'] * (7 * 24 * 60 * 60));

            $price = $orderGoodsListMap[$item['order_id'] . '-' . $item['goods_id']];

            $list[] = [
                'id' => $item['id'],
                'name' => $item['name'],
                'coverImg' => $item['cover_img'],
                'areaName' => $areaName,
                'cycle' => $cycle,
                'useCycle' => $useCycle < 0 ? 0 : $useCycle,
                'endTime' => $endDate,
                'price' => $price / 100,
                'quantity' => $item['quantity'],
                'status' => $item['status'],
            ];
        }

        return [
            'total' => $total,
            'totalQuantity' => intval($totalQuantity[0]['totalQuantity'] ?? 0),
            'list' => $list,
        ];
    }

    /**
     * 认养详情
     * @param array $params
     * @return array
     * @throws Exception
     */
    public function sponsorInfo(array $params): array
    {
        // 查询认养详情
        $userSponsorInfo = $this->getUserSponsorModel()->getItemById($params['id'], [
            'id', 'ranch_id', 'order_id', 'goods_id', 'name', 'cover_img', 'province_id', 'city_id', 'region_id', 'cycle', 'start_time', 'quantity', 'status'
        ]);
        if (empty($userSponsorInfo)) {
            throw new  SponsorException("用户认养记录不存在", SponsorException::CODE_4001);
        }

        $ranchData = $this->getStoreRanchModel()->getItemById($userSponsorInfo['ranch_id'], ['id', 'province_id', 'city_id', 'region_id']);

        // 查询认养图片
        $userSponsorImgInfo = $this->getUserSponsorImgModel()->getItemByUserSponsorId($userSponsorInfo['id'], [
            'id', 'user_sponsor_id', 'img_url'
        ]);

        $sponsorImgList = [];
        foreach ($userSponsorImgInfo as $item) {
            $sponsorImgList[] = [
                'id' => $item['id'],
                'imgUrl' => $item['img_url']
            ];
        }

        // 查询地区
        $areaName = '';
        if ($ranchData) {
            $areaIds = [$ranchData['province_id'], $ranchData['city_id'], $ranchData['region_id']];
            // 查询地区
            $areaList = $this->getAreaModel()->getAreaNameListByCodeList($areaIds);
            $areaList = array_column($areaList, 'name');
            $areaName = $this->getAreaName(...$areaList);
        }

        // 查询订单
        $orderGoodsInfo = $this->getSponsorOrderGoodsModel()->getItemByOrderIdAndGoodsId($userSponsorInfo['order_id'], $userSponsorInfo['goods_id'], [
            'id', 'order_id', 'goods_id', 'price'
        ]);

        // 周期(周)
        $cycle = ceil($userSponsorInfo['cycle'] / (7 * 24 * 60 * 60));
        // 已养周期
        $useCycle = floor((time() - strtotime($userSponsorInfo['start_time'])) / (7 * 24 * 60 * 60));

        $endDate = date('Y-m-d', strtotime($userSponsorInfo['start_time']) + $userSponsorInfo['cycle'] * (7 * 24 * 60 * 60));

        // 查询宰杀记录
        $slaughter = $this->getUserSlaughterRecordsModel()->getItemByUserIdAndUserSponsorId(27, $params['id'], [
            'id', 'type', 'status', 'remark'
        ]);

        // 判断宰杀记录是否为宰杀寄回
        if (!empty($slaughter)) {
            $slaughter['expressList'] = [];
            if ($slaughter['type'] == UserSlaughterRecordsModel::TYPE_SEND_BACK) {
                // 查询slaughter_send_back_records
                $slaughterSendBackRecords = $this->getSlaughterSendBackRecordsModel()->getItemByUserSlaughterRecordsId($slaughter['id'], [
                    'user_slaughter_records_id', 'slaughter_express_id'
                ]);
                if ($slaughterSendBackRecords) {
                    // 查询物流信息
                    $slaughterExpress = $this->getSlaughterExpressModel()->getItemById($slaughterSendBackRecords['slaughter_express_id'], [
                        'express_no', 'express_company_code', 'phone'
                    ]);

                    try {
                        $expressList = $this->getExpressApi()->query($slaughterExpress['express_company_code'], $slaughterExpress['express_no'], $slaughterExpress['phone']);
                    } catch (Exception $exception) {
                        throw new OtherException($exception->getMessage(), OtherException::CODE_1003);
                    }
                    $slaughter['expressNo'] = $slaughterExpress['express_no'];
                    $slaughter['expressList'] = $expressList;
                }
            }
        }


        return [
            'id' => $userSponsorInfo['id'],
            'name' => $userSponsorInfo['name'],
            'coverImg' => $userSponsorInfo['cover_img'],
            'areaName' => $areaName,
            'cycle' => $cycle,
            'useCycle' => $useCycle < 0 ? 0 : $useCycle,
            'endTime' => $endDate,
            'price' => $orderGoodsInfo['price'] / 100,
            'quantity' => $userSponsorInfo['quantity'],
            'status' => $userSponsorInfo['status'],
            'sponsorImgList' => $sponsorImgList,
            'slaughterInfo' => !empty($slaughter) ? $slaughter : null
        ];
    }

    /**
     * 积分记录列表
     * @return array
     * @throws Exception
     */
    public function integralRecordsList(): array
    {
        // 查询用户积分记录
        $userIntegralRecordsList = $this->getUserIntegralRecordsModel()->list(MvcHelper::getLoginUserId(), [
            'id', 'user_id', 'type', 'quantity', 'remark', 'created_at'
        ]);

        $list = [];
        foreach ($userIntegralRecordsList as $item) {
            $quantityText = '';
            switch ($item['type']) {
                case UserIntegralRecordsModel::TYPE_INCREMENT:
                    $quantityText = "+" . $item['quantity'];
                    break;
                case UserIntegralRecordsModel::TYPE_DECREMENT:
                    $quantityText = "-" . $item['quantity'];
                    break;
            }

            $list[] = [
                'id' => $item['id'],
                'quantity' => $quantityText,
                'remark' => $item['remark'],
                'created_at' => $item['created_at'],
            ];
        }

        return [
            'list' => $list
        ];
    }
}
